/*
 * SonarSource :: .NET :: Core
 * Copyright (C) 2014-2025 SonarSource Sàrl
 * mailto:info AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the Sonar Source-Available License for more details.
 *
 * You should have received a copy of the Sonar Source-Available License
 * along with this program; if not, see https://sonarsource.com/license/ssal/
 */
package org.sonarsource.dotnet.shared.plugins.protobuf;

import java.io.Serializable;
import java.util.HashSet;

import java.util.function.UnaryOperator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.measure.Metric;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.issue.NoSonarFilter;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.FileLinesContext;
import org.sonar.api.measures.FileLinesContextFactory;
import org.sonarsource.dotnet.protobuf.SonarAnalyzer.MetricsInfo;

/**
  This class is responsible for reading/importing all the metrics that were generated by the C#/VB.NET analyzers.
 */
public class MetricsImporter extends ProtobufImporter<MetricsInfo> {

  private final SensorContext context;
  private final FileLinesContextFactory fileLinesContextFactory;
  private final NoSonarFilter noSonarFilter;
  private static final Logger LOG = LoggerFactory.getLogger(MetricsImporter.class);

  public MetricsImporter(SensorContext context, FileLinesContextFactory fileLinesContextFactory, NoSonarFilter noSonarFilter,
    UnaryOperator<String> toRealPath) {
    super(MetricsInfo.parser(), context, MetricsInfo::getFilePath, toRealPath);
    this.context = context;
    this.fileLinesContextFactory = fileLinesContextFactory;
    this.noSonarFilter = noSonarFilter;
  }

  @Override
  void consumeFor(InputFile inputFile, MetricsInfo message) {
    saveMetric(context, inputFile, CoreMetrics.CLASSES, message.getClassCount());
    saveMetric(context, inputFile, CoreMetrics.STATEMENTS, message.getStatementCount());
    saveMetric(context, inputFile, CoreMetrics.FUNCTIONS, message.getFunctionCount());
    saveMetric(context, inputFile, CoreMetrics.COMPLEXITY, message.getComplexity());

    noSonarFilter.noSonarInFile(inputFile, new HashSet<>(message.getNoSonarCommentList()));

    FileLinesContext fileLinesContext = fileLinesContextFactory.createFor(inputFile);

    saveMetric(context, inputFile, CoreMetrics.COMMENT_LINES, message.getNonBlankCommentCount());

    int lineCount = inputFile.lines();
    for (int line : message.getCodeLineList()) {
      if (line <= lineCount) {
        fileLinesContext.setIntValue(CoreMetrics.NCLOC_DATA_KEY, line, 1);
      } else if (LOG.isDebugEnabled()) {
        LOG.debug("The code line number was out of the range. File {}, Line {}", inputFile.filename(), line);
      }
    }
    saveMetric(context, inputFile, CoreMetrics.NCLOC, message.getCodeLineCount());

    if (message.getCognitiveComplexity() >= 0) {
      saveMetric(context, inputFile, CoreMetrics.COGNITIVE_COMPLEXITY, message.getCognitiveComplexity());
    }

    for (Integer executableLineNumber : message.getExecutableLinesList()) {
      fileLinesContext.setIntValue(CoreMetrics.EXECUTABLE_LINES_DATA_KEY, executableLineNumber, 1);
    }

    fileLinesContext.save();
  }

  private static <T extends Serializable> void saveMetric(SensorContext context, InputFile inputFile, Metric<T> metric, T value) {
    context.<T>newMeasure()
      .on(inputFile)
      .forMetric(metric)
      .withValue(value)
      .save();
  }
}
